//file:kernel/mdl.c
//autor:jiangxinpeng
//time:2021.3.15
//uptime:2021.3.16
//copyright:(C) by jiangxinpeng,All right are reserved.

#include <os/debug.h>
#include <os/memcache.h>
#include <os/task.h>
#include <os/driver.h>
#include <os/mdl.h>
#include <os/virmem.h>
#include <os/spinlock.h>
#include <lib/list.h>
#include <lib/type.h>

LIST_HEAD(mdl_list_head);
DEFINE_SPIN_LOCK(mdl_lock);

mdl_t *MdlAlloc(uint64_t vbase, uint64_t len, void *ioreq)
{
    mdl_t *mdl;
    uint32_t pyaddr, pages, mapvaddr;

    if (!len || !vbase)
        return NULL;

    //alloc mdl object
    mdl = KMemAlloc(sizeof(mdl_t));
    if (!mdl)
        return NULL;

    //page align
    mdl->start_vbase = PTYPE(vbase & PAGEALIGN_MASK);
    mdl->bytes_offset = vbase - (uint64_t)(uint32_t)mdl->start_vbase;

    if (len > MDL_SIZE_MAX)
    {
        len = MDL_SIZE_MAX;
        KPrint(PRINT_INFO "mdl_alloc: len=%d too length\n", len);
    }
    mdl->bytes_count = len;
    mdl->task = NULL;
    list_init(&mdl->list);

    pyaddr = Vbase2Pybase(ADDR(mdl->start_vbase));
    pages = PageAlign(len) / PAGE_SIZE;
    KPrint(PRINT_DEBUG "mdl_alloc:viraddr=%x,pyaddr=%x\n", mdl->start_vbase, pyaddr);

    InterruptDisable();
    //alloc virtual base
    mapvaddr = VirBaseAlloc(len);
    if (!mapvaddr)
    {
        KMemFree(mdl);
        return NULL;
    }
    mdl->map_vbase = PTYPE(mapvaddr);

    //map fixed pages
    VMemMapFix(ADDR(mdl->map_vbase), pyaddr, len, PROTE_KERNEL | PROTE_WRITE);
    KPrint(PRINT_DEBUG "mdl_alloc: map succeed！pybase=%x,vbase=%x,len=%x\n", pyaddr, vbase, len);
    InterruptEnable();

    //if present ioreq
    if (ioreq)
    {
        list_add_tail(&mdl->list, &((io_request_t *)ioreq)->list);
    }
    return mdl;
}

//free a mdl
void MdlFree(mdl_t *mdl)
{
    if (!mdl)
        return;
    KPrint(PRINT_DEBUG "mdl free: vbase=%x,pybase=%x,len=%x\n", mdl->map_vbase, mdl->start_vbase, mdl->bytes_count);
    SpinLock(&mdl_lock);
    //unmap share memory
    VUnMemMap(ADDR(mdl->map_vbase), mdl->bytes_count, 1);
    VirBaseFree(ADDR(mdl->map_vbase), mdl->bytes_count);
    InterruptEnable();
    SpinUnlock(&mdl_lock);
    //free mdl object
    KMemFree(mdl);
}
